package com.ssy.lingxi.settle.accounts.serviceimpl;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ssy.lingxi.common.constant.Constants;
import com.ssy.lingxi.common.constant.basic.PurchaseContractPayTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementOrderTypeEnum;
import com.ssy.lingxi.common.constant.settle.SettlementTypeEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.PageData;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.component.redis.service.IRedisStringUtils;
import com.ssy.lingxi.contract.api.model.vo.request.ContractPageRequest;
import com.ssy.lingxi.contract.api.model.vo.response.ContractPageResponse;
import com.ssy.lingxi.dto.model.constant.ApplyAmountRowBillTypeEnum;
import com.ssy.lingxi.dto.request.*;
import com.ssy.lingxi.dto.response.ApplyAmountDetailSunVO;
import com.ssy.lingxi.dto.response.ApplyAmountDetailTotalSunVO;
import com.ssy.lingxi.dto.response.ApplyAmountSunVO;
import com.ssy.lingxi.order.api.model.vo.request.OrderBuyerSettlePageVO;
import com.ssy.lingxi.order.api.model.vo.response.OrderBuyerSettleQueryVO;
import com.ssy.lingxi.settle.accounts.entity.*;
import com.ssy.lingxi.settle.accounts.model.bo.ApplyAmountDetailBO;
import com.ssy.lingxi.settle.accounts.model.bo.InvoiceProveBO;
import com.ssy.lingxi.settle.accounts.model.constant.*;
import com.ssy.lingxi.settle.accounts.model.vo.request.*;
import com.ssy.lingxi.settle.accounts.model.vo.response.*;
import com.ssy.lingxi.settle.accounts.repository.*;
import com.ssy.lingxi.settle.accounts.service.IApplyAmountService;
import com.ssy.lingxi.settle.accounts.service.IBusinessReconciliationService;
import com.ssy.lingxi.settle.accounts.service.IFeignService;
import com.ssy.lingxi.settle.accounts.service.IMemberSettlementService;
import com.ssy.lingxi.settle.accounts.utils.NumberUtil;
import com.ssy.lingxi.settle.accounts.utils.OrderDateTimeUtil;
import com.ssy.lingxi.settle.accounts.utils.SerialNumberUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务请款单信息接口定义实现
 * @author Saske
 * @date 2021/12/20
 * @version 2.0.0
 */
@Service
public class ApplyAmountServiceImpl implements IApplyAmountService {

    @Resource
    private ApplyAmountRepository repository;

    @Resource
    private ApplyAmountRowRepository detailRepository;

    @Resource
    private ApplyAmountRowWriteOffRecordRepository detailRecordRepository;

    @Resource
    private BusinessReconciliationRepository reconciliationRepository;

    @Resource
    private BusinessReconciliationInvoiceRecordRepository invoiceRecordRepository;

//    @Resource
//    private IStatementCirculationRecordService recordService;

//    @Resource
//    private IMemberReceiptInvoiceService invoiceService;

    @Resource
    private IMemberSettlementService memberSettlementService;

    @Resource
    private IBusinessReconciliationService reconciliationService;

    @Resource
    private IFeignService feignService;

    @Resource
    private IRedisStringUtils redisStringUtils;

    @Resource
    private JPAQueryFactory jpaQueryFactory;


    /**
     * 新增请款单
     * @param sysUser saveVo
     * @return 请款单id
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> save(UserLoginCacheDTO sysUser, ApplyAmountSaveVO saveVO) {
        // step1.校验
        Wrapper<Void> checkResult = checkSaveDate(saveVO);
        if (checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }
        // step2: 保存请款单
        // 构建保存实体
        ApplyAmountDO entity = buildSaveEntity(sysUser, saveVO);
        // 保存请款
        repository.saveAndFlush(entity);

        // step3:更新非物料对账单请款单明细中用于核销的金额
        List<ApplyAmountRowWriteOffRecordSaveVO> writeOffRecords = saveVO.getRows().stream().filter(f -> !CollectionUtils.isEmpty(f.getWriteOffRecords())).flatMap(flatMap -> flatMap.getWriteOffRecords().stream()).collect(Collectors.toList());
        saveForWriteOffDetails(entity, writeOffRecords);
        // 成功保存请款单，回写对账单状态变为已请款,添加对账单流转记录
        if (ApplyAmountTypeEnum.MATERIAL.getCode().equals(saveVO.getApplyType()) && NumberUtil.notNullOrZero(saveVO.getReconciliationId())){
            Wrapper<Void> applyAmountWrapper =  reconciliationService.applyAmount(entity, saveVO.getReconciliationId(), sysUser);
            if (applyAmountWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
                Wrapper.fail(ResponseCode.getByCode(applyAmountWrapper.getCode()));
            }
        }
        return Wrapper.success();
    }

    /**
     * 校验保存请款单
     * @param saveVO 请款单
     */
    private Wrapper<Void> checkSaveDate(ApplyAmountSaveVO saveVO) {
        // step1:请款单非物料对账单时校验
        boolean notReconciliationFlag = !ApplyAmountTypeEnum.MATERIAL.getCode().equals(saveVO.getApplyType());
        if (notReconciliationFlag) {
            List<ApplyAmountRowSaveVO> rows = saveVO.getRows();
            // 过滤数据
            ApplyAmountDO entity = new ApplyAmountDO();
            entity.setId(0L);
            if (checkNotReconciliation(entity,rows)) {
                return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
            }

        } else {
            // 请款类型，类型是物料对账单是必须有对账单id、对账单编码
            if (ApplyAmountTypeEnum.MATERIAL.getCode().equals(saveVO.getApplyType())
                    && !(NumberUtil.notNullOrZero(saveVO.getReconciliationId()) &&
                    StringUtils.hasLength(saveVO.getReconciliationNo()))) {
                return Wrapper.fail(ResponseCode.SETTLE_ACCOUNTS_SERVICE_ERROR);
            }
            // 对账金额=请款金额+核销金额, 且请款金额要大于等于0

            if (saveVO.getRows().stream().anyMatch(f -> f.getReconciliationAmount()
                    .subtract(f.getWriteOffAmount())
                    .subtract(f.getApplyPayment())
                    .compareTo(BigDecimal.ZERO) < 0
                    && f.getApplyPayment().setScale(2, RoundingMode.HALF_UP).compareTo(new BigDecimal("0.00")) < 0)) {
                return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
            }
            BigDecimal bigDecimal = saveVO.getRows().stream().map(ApplyAmountRowSaveVO::getApplyPayment).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            if (bigDecimal.setScale(2, RoundingMode.HALF_UP).compareTo(new BigDecimal("0.00")) <=0) {
                return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
            }
        }
        return Wrapper.success();
    }

    /**
     * 保存更新用于核销得明细项
     * @param entity 请款单
     * @param writeOffRecords 请款单核销记录
     */
    private void saveForWriteOffDetails(ApplyAmountDO entity, List<ApplyAmountRowWriteOffRecordSaveVO> writeOffRecords) {
        if (ApplyAmountTypeEnum.MATERIAL.getCode().equals(entity.getApplyType())
                && !CollectionUtils.isEmpty(writeOffRecords)) {
            // 查询更新请款明细行
            Specification<ApplyAmountRowDO> writeOffRowSpecification = (root, query, cb) -> {
                CriteriaBuilder.In<Object> in = cb.in(root.get("id"));
                writeOffRecords.forEach(item -> in.value(item.getApplyRowId()));
                return in;
            };
            List<ApplyAmountRowDO> updateForWriteOffDetails = detailRepository.findAll(writeOffRowSpecification);
            updateForWriteOffDetails.forEach(item -> {
                // 获取新增用于核销金额
                BigDecimal addForWriteForAmount = writeOffRecords.stream()
                        .filter(f -> f.getApplyRowId().equals(item.getId()))
                        .map(ApplyAmountRowWriteOffRecordSaveVO::getWriteOffAmount)
                        .reduce(BigDecimal::add)
                        .orElse(new BigDecimal("0.00"));
                // 更新用于核销金额
                BigDecimal forWriteForAmount = item.getForWriteOffAmount().add(addForWriteForAmount);
                item.setForWriteOffAmount(forWriteForAmount);
                // 更新明细
                detailRepository.saveAndFlush(item);
            });
        }
    }

    /**
     * 根据前端提交请款数据和登录用户构建保存entity
     * @param sysUser 登录用户
     * @param saveVO  请款单保存参数
     * @return 保存entity
     */
    private ApplyAmountDO buildSaveEntity(UserLoginCacheDTO sysUser, ApplyAmountSaveVO saveVO) {
        ApplyAmountDO entity = new ApplyAmountDO();
        // 检查请款金额
        checkApplyAmountRowApplyPayment(saveVO.getApplyType(), saveVO.getRows(), entity);
        // 生成请款单编码
        String applyNo = SerialNumberUtils.getApplyAmountNo(redisStringUtils, Constants.SETTLEMENT_APPLY_AMOUNT_NO);
        entity.setApplyNo(applyNo);
        // 新增 默认待提交状态
        entity.setStatus(ApplyAmountStatusEnum.TO_SUBMIT.getCode());
        // 新增 默认内部状态 待提交状态
        entity.setInnerStatus(ApplyAmountStatusEnum.TO_SUBMIT.getCode());
        // 是否提交审核过，用于判断是否可以删除
        entity.setHasSubmit(false);
        // 请款类型,定义在ApplyAmountTypeEnum中
        entity.setApplyType(saveVO.getApplyType());
        // 请款单摘要
        entity.setApplyAbstract(saveVO.getApplyAbstract());
        // 收款方
        entity.setPayee(saveVO.getPayee());
        // 付款方 登录用户
        entity.setPayer(sysUser.getCompany());
        // 账号名称
        entity.setAccountName(saveVO.getAccountName());
        // 银行账号
        entity.setBankAccount(saveVO.getBankAccount());
        // 开户行
        entity.setBankDeposit(saveVO.getBankDeposit());
        // 请款金额
        entity.setApplyAmount(saveVO.getRows().stream().map(ApplyAmountRowSaveVO::getApplyPayment).reduce(BigDecimal.ZERO,BigDecimal::add));
        // 预计付款日期
        entity.setExpectPayTime(LocalDate.parse(saveVO.getExpectPayTime(), SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER).atStartOfDay());
        // 付款方式
        entity.setPayWay(saveVO.getPayWay());
        // 付款方式参数-月：付款方式为账期-月，显示月数
        entity.setPayMonth(saveVO.getPayMonth());
        // 付款方式参数-日：付款方式为账期-月或者账期-天，显示日期或天数
        entity.setPayDate(saveVO.getPayDate());
        // 支付方式,
        entity.setMoneyPayWay(saveVO.getMoneyPayWay());
        // 请款备注
        entity.setRemark(saveVO.getRemark());
        // 对应对账单ID
        entity.setReconciliationId(saveVO.getReconciliationId());
        // 对应对账单号
        entity.setReconciliationNo(saveVO.getReconciliationNo());
        // 单据时间
        entity.setCreateTime(LocalDateTime.now());
        // 买方（采购）会员Id
        entity.setBuyerMemberId(saveVO.getBuyerMemberId());
        // 买方（采购）会员角色Id
        entity.setBuyerRoleId(saveVO.getBuyerRoleId());
        // 卖方（供应）会员Id
        entity.setVendorMemberId(saveVO.getVendorMemberId());
        // 卖方（供应）会员角色Id
        entity.setVendorRoleId(saveVO.getVendorRoleId());
        List<ApplyAmountRowSaveVO> saveRows = saveVO.getRows();
        // 组装明细项
        List<ApplyAmountRowDO> rowList = buildSaveDetails(entity, saveRows);
        // 关联请款单行明细
        entity.setRows(rowList);
        return entity;
    }

    /**
     * 根据请款单数据和保存明细数据，构建保存明细项
     * @param entity 请款单据
     * @param saveRows 保存明细项
     * @return 保存明细项
     */
    private List<ApplyAmountRowDO> buildSaveDetails(ApplyAmountDO entity, List<ApplyAmountRowSaveVO> saveRows) {
        return saveRows.stream().map(r -> {
            // 请款单明细
            ApplyAmountRowDO row = new ApplyAmountRowDO();
            // 请款单编号
            row.setApplyNo(entity.getApplyNo());
            // 单据来源合同id
            row.setSourceContractId(r.getSourceContractId());
            // 单据编号
            row.setBillId(r.getBillId());
            // 单据编号
            row.setBillNo(r.getBillNo());
            row.setApplyType(entity.getApplyType());
            // 单据摘要
            row.setBillAbstract(r.getBillAbstract());
            // 单据类型，定义在ReconciliationTypeEnum中
            row.setBillType(r.getBillType());
            // 单据时间
            row.setBillTime(LocalDate.parse(r.getBillTime(),SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER).atStartOfDay());
            // 单据状态
            row.setBillStatus(r.getBillStatus());
            // 请款单状态
            row.setStatus(ApplyAmountStatusEnum.TO_SUBMIT.getCode());
            // 单据金额 - 数据来源
            row.setBillAmount(r.getBillAmount());
            // 单据行金额
            row.setBillRowAmount(Optional.ofNullable(r.getBillAmount()).orElse(new BigDecimal("0.00")));
            // 是否含税
            row.setHasTax(CommonBooleanEnum.YES.getCode());
            // 税率（百分比的分子部分）
            row.setTaxRate(r.getTaxRate());
            // 已付款
            row.setPaid(r.getPaid());
            // 已请款待付款
            row.setAppliedUnpaid(r.getAppliedUnpaid());
            // 对账金额
            row.setReconciliationAmount(r.getReconciliationAmount());
            // 请款金额
            row.setApplyPayment(r.getApplyPayment());
            // 核销金额
            row.setWriteOffAmount(r.getWriteOffAmount());
            // 用于核销金额默认新增时时0
            row.setForWriteOffAmount(new BigDecimal("0.00"));
            // 设置id
            row.setApply(entity);
            // 设置核销记录
            if (ApplyAmountTypeEnum.MATERIAL.getCode().equals(entity.getApplyType()) && !CollectionUtils.isEmpty(r.getWriteOffRecords())) {
                List<ApplyAmountRowWriteOffRecordDO> writeOffRecordDOS = r.getWriteOffRecords().stream().map(map -> {
                    ApplyAmountRowWriteOffRecordDO writeOffRecordDO = new ApplyAmountRowWriteOffRecordDO();
                    writeOffRecordDO.setApplyRow(row); // 核销请款行记录
                    writeOffRecordDO.setWriteOffRowRecord(map.getApplyRowId()); // 核销请款明细id
                    writeOffRecordDO.setWriteOffAmount(map.getWriteOffAmount());
                    return writeOffRecordDO;
                }).collect(Collectors.toList());
                row.setWriteOffRecords(writeOffRecordDOS);
            }
            // 保存明细项
            return row;
        }).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> update(UserLoginCacheDTO sysUser, ApplyAmountUpdateVO updateVO) {
        // step1：校验
        ApplyAmountDO entity = repository.findById(updateVO.getId()).orElse(null);
        if (Optional.ofNullable(entity)
                .map(ApplyAmountDO::getStatus)
                .map(status -> !status.equals(ApplyAmountStatusEnum.TO_SUBMIT.getCode()))
                .orElse(true)) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_UPDATE_NO_PERMISSION);
        }
        Wrapper<Void> checkResult = checkUpdateVO(updateVO, entity);
        if (checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }
        // step2: 先删除明细项
        deleteDetails(entity);
        entity.setApplyAbstract(updateVO.getApplyAbstract());
        // 请款金额
        entity.setApplyAmount(updateVO.getRows().stream().map(ApplyAmountRowSaveVO::getApplyPayment).reduce(BigDecimal.ZERO,BigDecimal::add));
        // 预计付款日期
        entity.setExpectPayTime(LocalDate.parse(updateVO.getExpectPayTime(), SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER).atStartOfDay());
        // 付款方式
        entity.setPayWay(updateVO.getPayWay());
        // 付款方式参数-月：付款方式为账期-月，显示月数
        entity.setPayMonth(updateVO.getPayMonth());
        // 付款方式参数-日：付款方式为账期-月或者账期-天，显示日期或天数
        entity.setPayDate(updateVO.getPayDate());
        // 支付方式,
        entity.setMoneyPayWay(updateVO.getMoneyPayWay());
        // 请款备注
        entity.setRemark(updateVO.getRemark());

        List<ApplyAmountRowSaveVO> saveRows = updateVO.getRows();
        List<ApplyAmountRowDO> saveDetailEntities = buildSaveDetails(entity, saveRows);
        entity.setRows(saveDetailEntities);
        repository.saveAndFlush(entity);
        // step3:更新非物料对账单请款单明细中用于核销的金额
        List<ApplyAmountRowWriteOffRecordSaveVO> writeOffRecords = updateVO.getRows().stream().filter(f -> !CollectionUtils.isEmpty(f.getWriteOffRecords())).flatMap(flatMap -> flatMap.getWriteOffRecords().stream()).collect(Collectors.toList());
        saveForWriteOffDetails(entity, writeOffRecords);
        return Wrapper.success();
    }

    /**
     * 校验修改请款单数据
     * @param updateVO 前端传递-请款单数据
     * @param entity 修改前数据
     */
    private Wrapper<Void> checkUpdateVO(ApplyAmountUpdateVO updateVO, ApplyAmountDO entity) {
        // step1:请款单非物料对账单时校验
        boolean notReconciliationFlag = !ApplyAmountTypeEnum.MATERIAL.getCode().equals(entity.getApplyType());
        if (notReconciliationFlag) {
            // 查询请款明细项、同单据类型、单据id、所有的请款明细记录
            List<ApplyAmountRowSaveVO> rows = updateVO.getRows();
            if (checkNotReconciliation(entity, rows)) {
                return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
            }
        } else {
            // 对账金额=请款金额+核销金额
            if (updateVO.getRows().stream().anyMatch(f -> f.getReconciliationAmount()
                    .subtract(f.getWriteOffAmount())
                    .subtract(f.getApplyPayment())
                    .compareTo(BigDecimal.ZERO) < 0
                    && f.getApplyPayment().setScale(2, RoundingMode.HALF_UP).compareTo(new BigDecimal("0.00")) < 0)) {
                return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
            }
            BigDecimal bigDecimal = updateVO.getRows().stream().map(ApplyAmountRowSaveVO::getApplyPayment).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
            if (bigDecimal.setScale(2, RoundingMode.HALF_UP).compareTo(new BigDecimal("0.00")) <=0) {
                return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
            }
        }
        return Wrapper.success();
    }

    private boolean checkNotReconciliation(ApplyAmountDO entity, List<ApplyAmountRowSaveVO> rows) {
        List<Long> billIds = rows.stream().map(ApplyAmountRowSaveVO::getBillId).collect(Collectors.toList());
        Specification<ApplyAmountRowDO> detailSpecification = (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            CriteriaBuilder.In<Object> in = cb.in(root.get("billId"));
            billIds.forEach(in::value);
            list.add(in);
            if (Optional.ofNullable(entity)
                    .map(ApplyAmountDO::getId)
                    .map(map -> map.compareTo(0L) > 0)
                    .orElse(false)) {
                list.add(cb.notEqual(root.get("apply").as(ApplyAmountDO.class), entity));
            }
            Predicate[] p = new Predicate[list.size()];
            return cb.and(list.toArray(p));
        };
        List<ApplyAmountRowDO> detailEntities = detailRepository.findAll(detailSpecification);
        // 过滤数据
        return rows.stream().anyMatch(f -> {
            // 获取单据类型、单据id、税率一样的汇总已申请金额
            BigDecimal otherApplyPayment = detailEntities.stream().filter(f2 -> f2.getBillType().equals(f.getBillType())
                            && f2.getBillId().equals(f.getBillId())
                            && f2.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(f.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0)
                    .map(ApplyAmountRowDO::getApplyPayment).reduce(BigDecimal.ZERO, BigDecimal::add);
            return f.getBillRowAmount()
                    .subtract(otherApplyPayment)
                    .subtract(f.getApplyPayment())
                    .compareTo(BigDecimal.ZERO) < 0;
        });
    }

    /**
     * 删除明细项
     * @param entity 请款单
     */
    private void deleteDetails(ApplyAmountDO entity) {
        // step1: 获取请款单明细
        Specification<ApplyAmountRowDO> detailSpecification = (root, query, cb) -> cb.equal(root.get("apply").as(ApplyAmountDO.class), entity);
        List<ApplyAmountRowDO> detailEntities = detailRepository.findAll(detailSpecification);
        // step2: 获取请款单明细核销记录
        List<ApplyAmountRowWriteOffRecordDO> detailWriteOffRecords = getWriteOffRecordDOS(detailEntities);
        // stop3:如果是物料对账单，更新（扣减用于核销金额）历史核销金额
        if (ApplyAmountTypeEnum.MATERIAL.getCode().equals(entity.getApplyType())
                && !CollectionUtils.isEmpty(detailWriteOffRecords)) {
            // 查询更新请款明细行
            Specification<ApplyAmountRowDO> writeOffRowSpecification = (root, query, cb) -> {
                CriteriaBuilder.In<Object> in = cb.in(root.get("id"));
                detailWriteOffRecords.forEach(item -> in.value(item.getWriteOffRowRecordId()));
                return in;
            };
            List<ApplyAmountRowDO> updateForWriteOffDetails = detailRepository.findAll(writeOffRowSpecification);
            updateForWriteOffDetails.forEach(item -> {
                // 获取新增用于核销金额
                BigDecimal subtractForWriteForAmount = detailWriteOffRecords.stream()
                        .filter(f -> f.getWriteOffRowRecordId().equals(item.getId())).findFirst()
                        .map(ApplyAmountRowWriteOffRecordDO::getWriteOffAmount)
                        .orElse(new BigDecimal("0.00"));
                // 更新用于核销金额
                BigDecimal forWriteForAmount = item.getForWriteOffAmount().subtract(subtractForWriteForAmount);
                item.setForWriteOffAmount(forWriteForAmount);
                // 更新明细
                detailRepository.saveAndFlush(item);
            });
        }
        // 删除子表
        detailRepository.deleteAll(detailEntities);
        detailRecordRepository.deleteAll(detailWriteOffRecords);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> delete(UserLoginCacheDTO user, Long id) {
        ApplyAmountDO entity = repository.findById(id).orElse(null);
        if (entity == null) {
            return Wrapper.success();
        }
        // 状态非代提交或者是物料对账单时，无权删除
        if (!ApplyAmountStatusEnum.TO_SUBMIT.getCode().equals(entity.getStatus())
                && ApplyAmountTypeEnum.MATERIAL.getCode().equals(entity.getApplyType())) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_DELETE_NO_PERMISSION);
        }
        // 1.查询明细项
        Specification<ApplyAmountRowDO> detailSpecification = (root, query, cb) -> cb.equal(root.get("apply").as(ApplyAmountDO.class), entity);
        List<ApplyAmountRowDO> detailEntities = detailRepository.findAll(detailSpecification);
        // 删除明细
        if (!CollectionUtils.isEmpty(detailEntities)) {
            detailRepository.deleteAll(detailEntities);
        }
        // 删除请款单
        repository.delete(entity);
        return Wrapper.success();
    }

    /**
     * 检查申请金额有没有超过明细订单总额
     * @param applyType 请款类型
     * @param rows 明细数据
     * @param entity 请款单
     */
    private void checkApplyAmountRowApplyPayment(Integer applyType, List<ApplyAmountRowSaveVO> rows, ApplyAmountDO entity) {
        // 1.查询明细数据，对应单据id的所有请款记录
        List<Long> billIds = rows.stream().map(ApplyAmountRowSaveVO::getBillId).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(billIds)) {
            throw new BusinessException(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_PARAMETER_ERROR);
        }
        // 查询单据id查询
        Specification<ApplyAmountRowDO> specification = (root, query, cb) -> {
            if (Objects.nonNull(entity.getId())) {
                query.where(cb.notEqual(root.get("apply"), entity));
            }
            CriteriaBuilder.In<Long> in = cb.in(root.get("billId").as(Long.class));
            billIds.forEach(in::value);
            CriteriaBuilder.In<Long> sourceContractId = cb.in(root.get("sourceContractId").as(Long.class));
            billIds.forEach(sourceContractId::value);
            Predicate and = cb.and(cb.or(in, sourceContractId));
            query.where(and);
            return query.getRestriction();
        };
        List<ApplyAmountRowDO> details = detailRepository.findAll(specification);
        long count = rows.stream().filter(f -> {
            BigDecimal billAmount = f.getBillAmount();
            BigDecimal otherAmount = details.stream()
                    .filter(f2 -> {
                        // 如果明细是的类型是合同的话
                        if (f.getBillType().equals(ApplyAmountRowBillTypeEnum.CONTRACT.getCode())) {
                            // 记录明细项中是订单 && 来源合同id和明细单据id一致，税率一直。
                            return  (f2.getBillType().equals(ApplyAmountRowBillTypeEnum.ORDER.getCode())
                                    && Objects.equals(f2.getSourceContractId(), f.getBillId())
                                    && f2.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                                    .compareTo(f.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0)
                                    //
                                    || (
                                    f2.getBillType().equals(ApplyAmountRowBillTypeEnum.CONTRACT.getCode())
                                            && Objects.equals(f2.getBillId(), f.getBillId())
                                            && f2.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                                            .compareTo(f.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0);

                        } else {
                            return  f2.getBillType().equals(f.getBillType())
                                    && f2.getBillId().equals(f.getBillId())
                                    && f2.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                                    .compareTo(f.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0;
                        }
                    }).map(ApplyAmountRowDO::getApplyPayment)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal applyPayment = f.getApplyPayment();
            BigDecimal subtract = billAmount.subtract(otherAmount).subtract(applyPayment);
            return Objects.equals(f.getBillType(), ApplyAmountRowBillTypeEnum.RETURN_REQUEST.getCode()) == (subtract.compareTo(new BigDecimal("0.00")) >= 0);
        }).count();
        if (count > 0) {
            throw new BusinessException(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
        }

        // 物料对账
        if(Objects.equals(applyType, ApplyAmountTypeEnum.MATERIAL.getCode())) {
            long count1 = rows.stream().filter(f -> {
                BigDecimal reconciliationAmount = f.getReconciliationAmount();
                BigDecimal applyPayment = f.getApplyPayment();
                BigDecimal writeOffAmount = f.getWriteOffAmount();
                return reconciliationAmount.setScale(2, RoundingMode.HALF_UP)
                        .compareTo(applyPayment.add(writeOffAmount).setScale(2, RoundingMode.HALF_UP)) != 0;
            }).count();
            if (count1 > 0) {
                throw new BusinessException(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_ROT_APPLY_PAYMENT_ERROR);
            }
        }
    }

    /**
     * 查询请款单详情
     * @param detailApplyAmountVO  请款单详情查询条件
     * @return 请款单详情
     */
    @Override
    public Wrapper<ApplyAmountDetailVO> getInfo(UserLoginCacheDTO sysUser, DetailApplyAmountVO detailApplyAmountVO) {
        // step1: 查询请款单
        ApplyAmountDO entity = repository.findById(detailApplyAmountVO.getApplyAmountId()).orElse(null);
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_BUSINESS_APPLY_AMOUNT_NOT_EXIST);
        }
        // step2: 查询请款单明细
        Specification<ApplyAmountRowDO> detailSpecification = (root, query, cb) -> cb.equal(root.get("apply").as(ApplyAmountDO.class), entity);
        List<ApplyAmountRowDO> detailEntities = detailRepository.findAll(detailSpecification);

        ApplyAmountDetailVO detailVO = new ApplyAmountDetailVO();
        // step3: 查询开票记录
        if (entity.getReconciliationId() != null) {
            // 查询对账单
            BusinessReconciliationDO reconciliationEntity = reconciliationRepository.findById(entity.getReconciliationId()).orElse(null);
            if (reconciliationEntity == null) {
                return Wrapper.fail(ResponseCode.SA_BUSINESS_APPLY_AMOUNT_NOT_EXIST);
            }
            Specification<BusinessReconciliationInvoiceRecordDO> invoiceRecordDOSpecification = (root, query, cb) -> cb.equal(root.get("reconciliation").as(BusinessReconciliationDO.class), reconciliationEntity);
            List<BusinessReconciliationInvoiceRecordDO> invoiceRecordEntities = invoiceRecordRepository.findAll(invoiceRecordDOSpecification);
            List<ReconciliationInvoiceNumber> numbers = invoiceRecordEntities.stream().map(map -> {
                ReconciliationInvoiceNumber item = new ReconciliationInvoiceNumber();
                item.setInvoiceNumber(map.getNumber());
                item.setInvoiceDate(map.getInvoiceDate());
                item.setInvoiceMoney(map.getInvoiceAmount());
                return item;
            }).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(numbers)) {
                ReconciliationInvoiceMessage invoiceMessages = new ReconciliationInvoiceMessage();
                invoiceMessages.setNumbers(numbers);
                detailVO.setInvoiceMessages(invoiceMessages);
            }
        }
        // step4: 组装数据
        detailVO.setId(entity.getId());
        detailVO.setApplyNo(entity.getApplyNo());
        detailVO.setStatus(entity.getStatus());
        detailVO.setStatusName(ApplyAmountStatusEnum.getNameByCode(entity.getStatus()));
        detailVO.setInnerStatus(entity.getInnerStatus());
        detailVO.setInnerStatusName(ApplyAmountStatusEnum.getNameByCode(entity.getInnerStatus()));
        detailVO.setApplyType(entity.getApplyType());
        detailVO.setApplyTypeName(ApplyAmountTypeEnum.getNameByCode(entity.getApplyType()));
        detailVO.setApplyAbstract(entity.getApplyAbstract());
        detailVO.setVendorMemberId(entity.getVendorMemberId());
        detailVO.setVendorRoleId(entity.getVendorRoleId());
        detailVO.setPayee(entity.getPayee());
        detailVO.setPayer(entity.getPayer());
        detailVO.setAccountName(entity.getAccountName());
        detailVO.setBankAccount(entity.getBankAccount());
        detailVO.setBankDeposit(entity.getBankDeposit());
        detailVO.setApplyAmount(entity.getApplyAmount());
        detailVO.setExpectPayTime(entity.getExpectPayTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
        detailVO.setPayWay(entity.getPayWay()); // 付款方式
        detailVO.setPayWayName(PurchaseContractPayTypeEnum.getNameByCode(entity.getPayWay()));
        detailVO.setPayMonth(entity.getPayMonth());
        detailVO.setPayDate(entity.getPayDate());
        detailVO.setMoneyPayWay(entity.getMoneyPayWay()); // 结算支付方式
        detailVO.setMoneyPayWayName(SettlementTypeEnum.getName(entity.getMoneyPayWay()));
        detailVO.setRemark(entity.getRemark());
        detailVO.setReconciliationId(entity.getReconciliationId());
        detailVO.setReconciliationNo(entity.getReconciliationNo());
        detailVO.setCreateTime(entity.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
        // 累计请款金额
        BigDecimal applyPayment = detailEntities.stream().map(ApplyAmountRowDO::getApplyPayment).reduce(BigDecimal.ZERO, BigDecimal::add);
        detailVO.setApplyPayment(applyPayment);
        // 累计核销金额
        BigDecimal writeOffAmount = detailEntities.stream().map(ApplyAmountRowDO::getWriteOffAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        detailVO.setWriteOffAmount(writeOffAmount);

        return Wrapper.success(detailVO);
    }

    /**
     * 查询请款单状态列表
     * @return 请款单状态列表
     */
    @Override
    public Wrapper<List<DropdownItem>> findStatusList(UserLoginCacheDTO sysUser) {
        return Wrapper.success(Arrays.stream(ApplyAmountStatusEnum.values()).map(e -> new DropdownItem(e.getCode(), e.getMessage())).collect(Collectors.toList()));
    }

    /**
     * 采购方提交请款单
     * @author Saske
     * @date 2021/12/23
     * @param sysUser submitVo
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> submit(UserLoginCacheDTO sysUser, ApplyAmountOperationVO submitVo) {
        // step1:修改请款单状态
        ApplyAmountDO entity = repository.findById(submitVo.getId()).orElse(null);
        if (entity == null){
            return Wrapper.fail(ResponseCode.SA_BUSINESS_APPLY_AMOUNT_NOT_EXIST);
        }
        if (!entity.getStatus().equals(ApplyAmountStatusEnum.TO_SUBMIT.getCode())){
            return Wrapper.fail(ResponseCode.SA_APPLY_AMOUNT_CAN_NOT_SUBMIT);
        }
        entity.setStatus(ApplyAmountStatusEnum.SUBMITTED.getCode());
        repository.saveAndFlush(entity);
        // step1: 查询请款明细
        Specification<ApplyAmountRowDO> detailSpecification = (root, query, cb) -> cb.equal(root.get("apply").as(ApplyAmountDO.class), entity);
        List<ApplyAmountRowDO> detailEntities = detailRepository.findAll(detailSpecification);

        // step2:新增开票管理记录
        // 组装开票信息
//        List<MemberReceiptInvoiceBaseVO> invoiceBaseVOS = buildMemberReceiptInvoiceBases(entity, detailEntities);
//        for (MemberReceiptInvoiceBaseVO invoiceBaseVO:
//        invoiceBaseVOS) {
//            invoiceService.save(invoiceBaseVO);
//        }
        // step3:新增应付账款
        MemberSettlementBaseVO settlementBaseVO = buildMemberSettlementBase(entity, detailEntities);

        memberSettlementService.save(settlementBaseVO);
        return Wrapper.success();
    }

    /**
     * 根据请款单构建应付账款
     * @param entity 请款单
     * @param detailEntities 请款单明细记录
     * @return 根据请款单构建应付账款
     */
    private MemberSettlementBaseVO buildMemberSettlementBase(ApplyAmountDO entity, List<ApplyAmountRowDO> detailEntities) {
        long settlementTimestamp = OrderDateTimeUtil.localDateTimeToTimestamp(LocalDateTime.now());
        long prePayTime = OrderDateTimeUtil.localDateTimeToTimestamp(entity.getExpectPayTime());
        MemberSettlementBaseVO saveVO = new MemberSettlementBaseVO();
        saveVO.setMemberId(entity.getVendorMemberId()); // 结算会员id(供应商)
        saveVO.setRoleId(entity.getVendorRoleId()); // 结算角色id（供应商）
        saveVO.setSettlementName(entity.getPayee()); // 结算方（供应商）
        saveVO.setPayMemberId(entity.getBuyerMemberId()); // 付款会员id(采购商)
        saveVO.setPayRoleId(entity.getBuyerRoleId()); // 付款角色id(采购商)
        saveVO.setPayName(entity.getPayer()); // 支付方(采购商)
        saveVO.setSettlementWay(SettlementWayEnum.getNameByPurchaseContractPayTypeEnumCode(entity.getPayWay())); // 结算方式
        saveVO.setOrderType(SettlementOrderTypeEnum.APPLY_AMOUNT.getCode()); // 结算单据类型 默认请款单
        saveVO.setTotalCount((long) detailEntities.size()); // 总单数
        BigDecimal amount = detailEntities.stream().map(ApplyAmountRowDO::getApplyPayment).reduce(BigDecimal.ZERO, BigDecimal::add);
        saveVO.setAmount(amount.doubleValue()); // 结算金额
        saveVO.setSettlementDate(settlementTimestamp); // 结算日期
        saveVO.setSettlementTime(settlementTimestamp); // 结算时间
        saveVO.setPrePayTime(prePayTime); // 预付款时间
        saveVO.setPayWay(entity.getMoneyPayWay()); // 支付方式
        saveVO.setStrategyId(0L);
        // 结算单据信息
        SettlementOrderBaseVO orderBaseVO = new SettlementOrderBaseVO();
        orderBaseVO.setOrderId(entity.getId());
        orderBaseVO.setOrderNo(entity.getApplyNo());
        saveVO.setOrderVO(orderBaseVO);
        saveVO.setStatus(SettlementStatusEnum.TO_BE_PAY.getCode()); // 结算状态
        return saveVO;
    }

//    /**
//     * 根据请款单构建开票管理记录
//     * @param entity 请款单
//     * @param detailEntities 请款单明细记录
//     * @return 开票管理结记录
//     */
//    private List<MemberReceiptInvoiceBaseVO> buildMemberReceiptInvoiceBases(ApplyAmountDO entity, List<ApplyAmountRowDO> detailEntities) {
//        // step1: 组装开票记录
//        return detailEntities.stream().filter(f -> f.getApplyPayment().compareTo(BigDecimal.ZERO) > 0).map(map -> {
//            MemberReceiptInvoiceBaseVO saveVO = new MemberReceiptInvoiceBaseVO();
//            // 会员id(供应商)
//            saveVO.setMemberId(entity.getVendorMemberId());
//            // 角色id(供应商)
//            saveVO.setRoleId(entity.getVendorMemberId());
//            // 支付会员id(采购商)
//            saveVO.setPayMemberId(entity.getBuyerMemberId());
//            // 支付角色id(采购商)
//            saveVO.setPayRoleId(entity.getBuyerRoleId());
//            // 单据编码
//            saveVO.setOrderNo(entity.getApplyNo());
//            // 单据摘要
//            saveVO.setOrderAbstract(entity.getApplyAbstract());
//            // 单据类型 默认请款
//            saveVO.setSettlementOrderType(SettlementOrderTypeEnum.APPLY_AMOUNT.getCode());
//            // 订单类型 默认其他
//            saveVO.setOrderType(ReceiptInvoiceOrderTypeEnum.OTHER.getCode());
//            // 单据日期
//            saveVO.setOrderTime(entity.getCreateTime().toInstant(OffsetDateTime.now().getOffset()).toEpochMilli());
//            // 单据明细金额
//            saveVO.setOrderAmount(map.getApplyPayment().doubleValue());
//             saveVO.setBatch(1);
//             saveVO.setPayNode("结算后支付");
//            // 支付金额
//             saveVO.setPayAmount(map.getApplyPayment().doubleValue());
//            // 支付时间
//            // saveVO.setPayTime(0L);
//            // 开票状态 默认未开票
//            saveVO.setInvoiceStatus(ReceiptInvoiceStatusEnum.NO.getCode());
//            // 支付状态
//            saveVO.setPayStatus(ReceiptInvoicePayStatusEnum.NO.getCode());
//            // 关联数据id
//            saveVO.setDataId(entity.getId());
//            // 是否含税
//            Integer isHasTax = map.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(BigDecimal.ZERO) > 0 ? CommonBooleanEnum.YES.getCode() : CommonBooleanEnum.NO.getCode();
//            saveVO.setIsHasTax(isHasTax);
//            // 税率
//            saveVO.setTaxRate(map.getTaxRate().doubleValue());
//            return saveVO;
//        }).collect(Collectors.toList());
//    }

    /**
     * 分页查询请款单列表-采购方
     * @param sysUser   用户信息
     * @param queryVO   对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<ApplyAmountPageVO>> pageBuyerApplyAmount(UserLoginCacheDTO sysUser, ApplyAmountQueryVO queryVO) {
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        List<com.querydsl.core.types.Predicate> queryList = new ArrayList<>();
        // 合同id
        if (Objects.nonNull(queryVO.getContractId())) {
            queryList.add(applyAmountRowDO.sourceContractId.eq(queryVO.getContractId()));
            BooleanExpression or = applyAmountDO.buyerMemberId.eq(sysUser.getMemberId()).and(applyAmountDO.buyerRoleId.eq(sysUser.getMemberRoleId()))
                    .or(applyAmountDO.vendorMemberId.eq(sysUser.getMemberId()).and(applyAmountDO.vendorRoleId.eq(sysUser.getMemberRoleId())));
            queryList.add(or);
        } else {
            queryList.add(applyAmountDO.buyerMemberId.eq(sysUser.getMemberId()));
            queryList.add(applyAmountDO.buyerRoleId.eq(sysUser.getMemberRoleId()));
        }
        //请款单号
        if (StringUtils.hasText(queryVO.getApplyNo())){
            queryList.add(applyAmountDO.applyNo.like("%"+queryVO.getApplyNo().trim()+"%"));
        }

        //请款单摘要
        if(StringUtils.hasText(queryVO.getApplyAbstract())) {
            queryList.add(applyAmountDO.applyAbstract.like( "%" + queryVO.getApplyAbstract().trim() + "%"));
        }

        //付款方
        if(StringUtils.hasText(queryVO.getPayee())) {
            queryList.add(applyAmountDO.payer.like("%" + queryVO.getPayee().trim() + "%"));
        }

        //请款单状态
        if(NumberUtil.notNullOrZero(queryVO.getStatus())) {
            queryList.add(applyAmountDO.status.eq(queryVO.getStatus()));
        }

        //请款单单据起始时间
        if (StringUtils.hasText(queryVO.getCreateTimeStart())) {
            queryList.add(applyAmountDO.createTime.goe(LocalDateTime.parse(queryVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        //请款单单据结束时间
        if (StringUtils.hasText(queryVO.getCreateTimeEnd())) {
            queryList.add(applyAmountDO.createTime.loe(LocalDateTime.parse(queryVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        //请款单预计付款日期起始时间
        if (StringUtils.hasText(queryVO.getExpectPayTimeStart())) {
            queryList.add(applyAmountDO.expectPayTime.goe(LocalDateTime.parse(queryVO.getExpectPayTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        //请款单预计付款日期结束时间
        if (StringUtils.hasText(queryVO.getExpectPayTimeEnd())) {
            queryList.add(applyAmountDO.expectPayTime.loe(LocalDateTime.parse(queryVO.getExpectPayTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }
        JPAQuery<ApplyAmountDO> jpaQuery = jpaQueryFactory.select(applyAmountDO).distinct()
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id)).where(queryList.toArray(new com.querydsl.core.types.Predicate[0]));
        long total = jpaQuery.fetchCount();
        List<ApplyAmountDO> fetch = jpaQuery.limit(queryVO.getPageSize()).offset(queryVO.getCurrentOffset()).fetch();
        List<ApplyAmountPageVO> resultList = fetch.stream().map(r ->{
            ApplyAmountPageVO vo = new ApplyAmountPageVO();
            vo.setId(r.getId());
            vo.setApplyNo(r.getApplyNo());
            vo.setApplyAbstract(r.getApplyAbstract());
            vo.setApplyType(r.getApplyType());
            vo.setApplyTypeName(ApplyAmountTypeEnum.getNameByCode(r.getApplyType()));
            vo.setPayer(r.getPayer());
            vo.setPayee(r.getPayee());
            vo.setApplyAmount(r.getApplyAmount());
            vo.setExpectPayTime(r.getExpectPayTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setPayTime(Optional.ofNullable(r.getPayTime()).map(map -> map.format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER)).orElse(""));
            vo.setCreateTime(r.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setStatus(r.getStatus());
            vo.setStatusName(ApplyAmountStatusEnum.getNameByCode(r.getStatus()));
            boolean hasSubmit = !r.getApplyType().equals(ApplyAmountTypeEnum.MATERIAL.getCode()) && r.getStatus().equals(ApplyAmountStatusEnum.TO_SUBMIT.getCode());
            vo.setHasSubmit(hasSubmit);
            return vo;
        }).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(total, resultList));
    }

    /**
     * 分页查询待新增请款单列表-采购方
     * @param sysUser   用户信息
     * @param queryVO   对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<ApplyAmountPageVO>> pageToAddApplyAmount(UserLoginCacheDTO sysUser, ApplyAmountQueryVO queryVO) {

        Pageable pageable = PageRequest.of(queryVO.getCurrent() -1, queryVO.getPageSize(), Sort.by("id").descending());
        Specification<ApplyAmountDO> specification = (root, query, criteriaBuilder) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(criteriaBuilder.equal(root.get("buyerMemberId").as(Long.class), sysUser.getMemberId()));
            list.add(criteriaBuilder.equal(root.get("buyerRoleId").as(Long.class), sysUser.getMemberRoleId()));

            //采购方待新增请款单列表只查询待提交状态的请款单
            list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), ApplyAmountStatusEnum.TO_SUBMIT.getCode()));

            //请款单号
            if (StringUtils.hasLength(queryVO.getApplyNo())){
                list.add(criteriaBuilder.like(root.get("applyNo").as(String.class),"%"+queryVO.getApplyNo().trim()+"%"));
            }

            //请款单摘要
            if(StringUtils.hasLength(queryVO.getApplyAbstract())) {
                list.add(criteriaBuilder.like(root.get("applyAbstract").as(String.class), "%" + queryVO.getApplyAbstract().trim() + "%"));
            }

            //付款方
            if(StringUtils.hasLength(queryVO.getPayee())) {
                list.add(criteriaBuilder.like(root.get("payee").as(String.class), "%" + queryVO.getPayee().trim() + "%"));
            }

            //请款单状态
            if(NumberUtil.notNullOrZero(queryVO.getStatus())) {
                list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), queryVO.getStatus()));
            }

            //请款单单据起始时间
            if (StringUtils.hasLength(queryVO.getCreateTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(queryVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //请款单单据结束时间
            if (StringUtils.hasLength(queryVO.getCreateTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("createTime").as(LocalDateTime.class), LocalDateTime.parse(queryVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //请款单预计付款日期起始时间
            if (StringUtils.hasLength(queryVO.getExpectPayTimeStart())) {
                list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("expectPayTime").as(LocalDateTime.class), LocalDateTime.parse(queryVO.getExpectPayTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }

            //请款单预计付款日期结束时间
            if (StringUtils.hasLength(queryVO.getExpectPayTimeEnd())) {
                list.add(criteriaBuilder.lessThanOrEqualTo(root.get("expectPayTime").as(LocalDateTime.class), LocalDateTime.parse(queryVO.getExpectPayTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
            }
            // 是否不是物料单
            if (Optional.ofNullable(queryVO.getHasReconciliationBillFlag())
                    .map(map -> CommonBooleanEnum.YES.getCode().equals(map))
                    .orElse(false)) {
                list.add(criteriaBuilder.notEqual(root.get("applyType").as(Integer.class), ApplyAmountTypeEnum.MATERIAL));
            }

            Predicate[] p = new Predicate[list.size()];
            return criteriaBuilder.and(list.toArray(p));
        };

        return this.assembleData(repository.findAll(specification,pageable));
    }

    /**
     * 分页查询请款单列表-供应商
     * @param sysUser   用户信息
     * @param queryVO   对账单列表查询条件VO
     * @return 查询结果
     */
    @Override
    public Wrapper<PageData<ApplyAmountPageVO>> pageVendorApplyAmount(UserLoginCacheDTO sysUser, ApplyAmountQueryVO queryVO) {
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        List<com.querydsl.core.types.Predicate> queryList = new ArrayList<>();
        queryList.add(applyAmountDO.vendorMemberId.eq(sysUser.getMemberId()));
        queryList.add(applyAmountDO.vendorRoleId.eq(sysUser.getMemberRoleId()));

        //供应商屏蔽待提交状态的请款单
        queryList.add(applyAmountDO.status.ne(ApplyAmountStatusEnum.TO_SUBMIT.getCode()));

        // 合同id
        if (Objects.nonNull(queryVO.getContractId())) {
            queryList.add(applyAmountRowDO.sourceContractId.eq(queryVO.getContractId()));
        }

        //请款单号
        if (StringUtils.hasText(queryVO.getApplyNo())){
            queryList.add(applyAmountDO.applyNo.like("%"+queryVO.getApplyNo().trim()+"%"));
        }
        //请款单摘要
        if(StringUtils.hasText(queryVO.getApplyAbstract())) {
            queryList.add(applyAmountDO.applyAbstract.like( "%" + queryVO.getApplyAbstract().trim() + "%"));
        }
//付款方
        if(StringUtils.hasText(queryVO.getPayee())) {
            queryList.add(applyAmountDO.payer.like("%" + queryVO.getPayee().trim() + "%"));
        }

        //请款单状态
        if(NumberUtil.notNullOrZero(queryVO.getStatus())) {
            queryList.add(applyAmountDO.status.eq(queryVO.getStatus()));
        }

        //请款单单据起始时间
        if (StringUtils.hasText(queryVO.getCreateTimeStart())) {
            queryList.add(applyAmountDO.createTime.goe(LocalDateTime.parse(queryVO.getCreateTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        //请款单单据结束时间
        if (StringUtils.hasText(queryVO.getCreateTimeEnd())) {
            queryList.add(applyAmountDO.createTime.loe(LocalDateTime.parse(queryVO.getCreateTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        //请款单预计付款日期起始时间
        if (StringUtils.hasText(queryVO.getExpectPayTimeStart())) {
            queryList.add(applyAmountDO.expectPayTime.goe(LocalDateTime.parse(queryVO.getExpectPayTimeStart().concat(" 00:00:00"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }

        //请款单预计付款日期结束时间
        if (StringUtils.hasText(queryVO.getExpectPayTimeEnd())) {
            queryList.add(applyAmountDO.expectPayTime.loe(LocalDateTime.parse(queryVO.getExpectPayTimeEnd().concat(" 23:59:59"), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))));
        }
        JPAQuery<ApplyAmountDO> jpaQuery = jpaQueryFactory.select(applyAmountDO).distinct()
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id)).where(queryList.toArray(new com.querydsl.core.types.Predicate[0]));
        long total = jpaQuery.fetchCount();
        List<ApplyAmountDO> fetch = jpaQuery.limit(queryVO.getPageSize()).offset(queryVO.getCurrentOffset()).fetch();
        List<ApplyAmountPageVO> resultList = fetch.stream().map(r ->{
            ApplyAmountPageVO vo = new ApplyAmountPageVO();
            vo.setId(r.getId());
            vo.setApplyNo(r.getApplyNo());
            vo.setApplyAbstract(r.getApplyAbstract());
            vo.setApplyType(r.getApplyType());
            vo.setApplyTypeName(ApplyAmountTypeEnum.getNameByCode(r.getApplyType()));
            vo.setPayer(r.getPayer());
            vo.setPayee(r.getPayee());
            vo.setApplyAmount(r.getApplyAmount());
            vo.setExpectPayTime(r.getExpectPayTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setPayTime(Optional.ofNullable(r.getPayTime()).map(map -> map.format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER)).orElse(""));
            vo.setCreateTime(r.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setStatus(r.getStatus());
            vo.setStatusName(ApplyAmountStatusEnum.getNameByCode(r.getStatus()));
            boolean hasSubmit = !r.getApplyType().equals(ApplyAmountTypeEnum.MATERIAL.getCode()) && r.getStatus().equals(ApplyAmountStatusEnum.TO_SUBMIT.getCode());
            vo.setHasSubmit(hasSubmit);
            return vo;
        }).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(total, resultList));
    }

    @Override
    public Wrapper<List<ApplyAmountRowSaveVO>> generateSaveApplyAmountRows(ApplyAmountRowGenerateVO request) {
        // step1:查询对应订单id集合的数据
        List<Long> billIds = request.getRows().stream().map(ApplyAmountRowSaveVO::getBillId).collect(Collectors.toList());
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        List<ApplyAmountDetailBO> detailDOS = jpaQueryFactory.select(Projections.constructor(ApplyAmountDetailBO.class,
                        applyAmountRowDO.billId,
                        applyAmountRowDO.billType,
                        applyAmountRowDO.taxRate,
                        applyAmountDO.status,
                        applyAmountRowDO.applyPayment))
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                .where(applyAmountRowDO.billId.in(billIds)).fetch();
        // step2.组装过滤获取可添加请款明细数据
        List<ApplyAmountRowSaveVO> data = request.getRows().stream().peek(peek -> {
            // 统计已付款金额，单据状态已付款、单据id、单据类型、单据税率
            BigDecimal paid = detailDOS.stream()
                    .filter(f -> ApplyAmountStatusEnum.PAID.getCode().equals(f.getStatus())
                            && f.getBillId().equals(peek.getBillId())
                            && f.getBillType().equals(peek.getBillType())
                            && f.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(peek.getTaxRate().setScale(2, RoundingMode.HALF_UP)) ==0)
                    .map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal appliedUnpaid =  detailDOS.stream()
                    .filter(f -> !ApplyAmountStatusEnum.PAID.getCode().equals(f.getStatus())
                            && f.getBillId().equals(peek.getBillId())
                            && f.getBillType().equals(peek.getBillType())
                            && f.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(peek.getTaxRate().setScale(2, RoundingMode.HALF_UP)) ==0)
                    .map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal applyPayment = peek.getBillAmount().subtract(paid).subtract(appliedUnpaid);
            peek.setPaid(paid);
            peek.setAppliedUnpaid(appliedUnpaid);
            peek.setApplyPayment(applyPayment);
        }).collect(Collectors.toList());
        List<ApplyAmountRowSaveVO> successList = data.stream().filter(f -> f.getApplyPayment().compareTo(new BigDecimal("0.00")) > 0).collect(Collectors.toList());
        // 减去可请款数据
        data.removeAll(successList);
        String errorNos = data.stream().map(ApplyAmountRowSaveVO::getBillNo).collect(Collectors.joining(","));
        Wrapper<List<ApplyAmountRowSaveVO>> result =  Wrapper.success(successList);
        result.setMessage("不满足请款单据编码：" + errorNos);
        return result;
    }

    @Override
    public Wrapper<PageData<ApplyAmountRowWriteOffRecordSaveVO>> findCanApplyAmountRows(CanApplyAmountRowQueryParameterVO queryParameter) {
        // step1: 获取定义关联表
        QApplyAmountDO qApplyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO qApplyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        QApplyAmountRowWriteOffRecordDO qApplyAmountRowWriteOffRecordDO = QApplyAmountRowWriteOffRecordDO.applyAmountRowWriteOffRecordDO;
        // step2: 拼接查询语句
        BooleanBuilder otherQueryBuilder = new BooleanBuilder();
        BooleanBuilder sub1QueryBuilder = new BooleanBuilder();
        List<BooleanBuilder> subQueryBuilderList = new ArrayList<>();
        sub1QueryBuilder.and(qApplyAmountRowDO.billType.eq(ApplyAmountRowBillTypeEnum.ORDER.getCode()));
        sub1QueryBuilder.and(qApplyAmountRowDO.billId.eq(queryParameter.getBillId()));
        subQueryBuilderList.add(sub1QueryBuilder);
        if (queryParameter.getSourceContractId().compareTo(0L) > 0) {
            BooleanBuilder sub2QueryBuilder = new BooleanBuilder();
            sub2QueryBuilder.and(qApplyAmountRowDO.billType.eq(ApplyAmountRowBillTypeEnum.CONTRACT.getCode()));
            sub2QueryBuilder.and(qApplyAmountRowDO.billId.eq(queryParameter.getSourceContractId()));
            subQueryBuilderList.add(sub2QueryBuilder);
        }
        BooleanBuilder[] subQueryBuilderArrays = new BooleanBuilder[subQueryBuilderList.size()];
        subQueryBuilderList.toArray(subQueryBuilderArrays);
        otherQueryBuilder.andAnyOf(subQueryBuilderArrays);

        JPAQuery<ApplyAmountRowWriteOffRecordSaveVO> query = jpaQueryFactory.select(
                        Projections.constructor(ApplyAmountRowWriteOffRecordSaveVO.class,
                                qApplyAmountDO.id,
                                qApplyAmountDO.applyNo,
                                qApplyAmountDO.applyType,
                                qApplyAmountDO.applyAbstract,
                                qApplyAmountRowDO.id,
                                qApplyAmountRowDO.billId,
                                qApplyAmountRowDO.billNo,
                                qApplyAmountRowDO.billAbstract,
                                qApplyAmountRowDO.billTime,
                                qApplyAmountRowDO.billStatus,
                                qApplyAmountRowDO.taxRate,
                                qApplyAmountRowDO.applyPayment,
                                qApplyAmountRowWriteOffRecordDO.writeOffAmount.sum().coalesce(BigDecimal.ZERO)))
                .from(qApplyAmountDO).distinct()
                .leftJoin(qApplyAmountRowDO).on(qApplyAmountRowDO.apply.id.eq(qApplyAmountDO.id))
                .leftJoin(qApplyAmountRowWriteOffRecordDO).on(qApplyAmountRowWriteOffRecordDO.writeOffRowRecordId.eq(qApplyAmountRowDO.id))
                .where(qApplyAmountDO.applyType.ne(ApplyAmountTypeEnum.MATERIAL.getCode()),
                        qApplyAmountDO.status.eq(ApplyAmountStatusEnum.PAID.getCode()),
                        qApplyAmountRowDO.taxRate.eq(queryParameter.getTaxRate()));
        if (queryParameter.getNotQueryDetailId().compareTo(0L) > 0) {
            query.where(qApplyAmountRowDO.id.ne(queryParameter.getNotQueryDetailId()));
        }
        query.where(otherQueryBuilder)
                .groupBy(qApplyAmountDO.id,
                    qApplyAmountDO.applyNo,
                    qApplyAmountDO.applyType,
                    qApplyAmountDO.applyAbstract,
                    qApplyAmountRowDO.id,
                    qApplyAmountRowDO.billId,
                    qApplyAmountRowDO.billNo,
                    qApplyAmountRowDO.billAbstract,
                    qApplyAmountRowDO.billTime,
                    qApplyAmountRowDO.status,
                    qApplyAmountRowDO.applyPayment,
                    qApplyAmountRowDO.taxRate)
            .having(qApplyAmountRowDO.applyPayment.gt(qApplyAmountRowWriteOffRecordDO.writeOffAmount.sum().coalesce(BigDecimal.ZERO)));
        long totalCount = query.fetch().size();
        query.limit(queryParameter.getPageSize()).offset(queryParameter.getCurrentOffset());
        PageData<ApplyAmountRowWriteOffRecordSaveVO> page = new PageData<>(totalCount, query.fetch());
        return Wrapper.success(page);
    }

    @Override
    public Wrapper<List<DropdownItem>> findTypeList() {
        return Wrapper.success(Arrays.stream(ApplyAmountTypeEnum.values()).filter(f-> f!=ApplyAmountTypeEnum.MATERIAL).map(e -> new DropdownItem(e.getCode(), e.getMessage())).collect(Collectors.toList()));

    }

    @Override
    public Wrapper<PageData<ApplyAmountRowDetailVO>> findDetails(UserLoginCacheDTO sysUser, DetailApplyAmountVO queryParameter) {
        // step1: 查询明细项记录
        Pageable pageable = PageRequest.of(queryParameter.getCurrent() -1, queryParameter.getPageSize(), Sort.by("id").descending());
        ApplyAmountDO entity = new ApplyAmountDO();
        entity.setId(queryParameter.getApplyAmountId());
        Specification<ApplyAmountRowDO> detailSpecification = (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("apply").as(ApplyAmountDO.class), entity);
        Page<ApplyAmountRowDO> detailEntitiesPage = detailRepository.findAll(detailSpecification, pageable);
        // step2: 查询明细报销记录
        // 没有记录直接返回
        if (detailEntitiesPage.getTotalElements() <= 0) {
            Wrapper.success(new PageData<>(detailEntitiesPage.getTotalElements(),new ArrayList<>()));
        }
        List<ApplyAmountRowDO> detailEntities = detailEntitiesPage.getContent();
        ApplyAmountDO helpEntity = detailEntities.stream().findFirst().map(ApplyAmountRowDO::getApply).orElse(null);
        Map<Long, List<ApplyAmountRowWriteOffRecordDO>> recordMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(detailEntities)) {
            List<ApplyAmountRowWriteOffRecordDO> recordEntities = getWriteOffRecordDOS(detailEntities);
            Map<Long, List<ApplyAmountRowWriteOffRecordDO>> tempRecordMap = recordEntities.stream().collect(Collectors.groupingBy(K -> K.getApplyRow().getId()));
            recordMap.putAll(tempRecordMap);
        }
        // step3: 查询明细核销记录对应的请款明细
        List<Long> writeOffRowRecordIds = recordMap.values().stream().flatMap(Collection::stream).map(ApplyAmountRowWriteOffRecordDO::getWriteOffRowRecordId).collect(Collectors.toList());
        List<ApplyAmountRowWriteOffRecordSaveVO> writeOffRowRecordEntities = new ArrayList<>();
        if (!CollectionUtils.isEmpty(writeOffRowRecordIds)) {
            QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
            QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
            writeOffRowRecordEntities = jpaQueryFactory.select(Projections.constructor(ApplyAmountRowWriteOffRecordSaveVO.class,
                            applyAmountDO.id,
                            applyAmountDO.applyNo,
                            applyAmountDO.applyType,
                            applyAmountDO.applyAbstract,
                            applyAmountRowDO.id,
                            applyAmountRowDO.billId,
                            applyAmountRowDO.billNo,
                            applyAmountRowDO.billAbstract,
                            applyAmountRowDO.billTime,
                            applyAmountRowDO.billStatus,
                            applyAmountRowDO.taxRate,
                            applyAmountRowDO.applyPayment,
                            applyAmountRowDO.forWriteOffAmount))
                    .from(applyAmountDO)
                    .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                    .where(applyAmountRowDO.id.in(writeOffRowRecordIds)).fetch();
        }
        // step4: 查询是否还可核销
        // 单据id
        List<Long> billIds = detailEntities.stream().map(ApplyAmountRowDO::getBillId).collect(Collectors.toList());
        // 来源合同id
        List<Long> sourceContractIds = detailEntities.stream().map(map -> Optional.ofNullable(map.getSourceContractId()).orElse(0L)).filter(f -> f.compareTo(0L)>0).collect(Collectors.toList());
        // 添加查询合同类明细的单据id
        billIds.addAll(sourceContractIds);
        List<ApplyAmountRowDO> canWriteOffDetailEntities = new ArrayList<>();
        if (Optional.ofNullable(helpEntity).map(ApplyAmountDO::getApplyType).map(map -> Objects.equals(map,ApplyAmountTypeEnum.MATERIAL.getCode())).orElse(false)  && !CollectionUtils.isEmpty(billIds)) {
            // 查询订单的
            QApplyAmountDO qApplyAmountDO = QApplyAmountDO.applyAmountDO;
            QApplyAmountRowDO qApplyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;

            List<com.querydsl.core.types.Predicate>  predicateList = new ArrayList<>();
            predicateList.add(qApplyAmountRowDO.billId.in(billIds));
            if (!CollectionUtils.isEmpty(sourceContractIds)) {
                predicateList.add(qApplyAmountRowDO.sourceContractId.in(sourceContractIds));
            }
            com.querydsl.core.types.Predicate[] predicates = new com.querydsl.core.types.Predicate[predicateList.size()];
            predicateList.toArray(predicates);
            List<ApplyAmountRowDO> list = jpaQueryFactory.select(qApplyAmountRowDO)
                    .from(qApplyAmountDO)
                    .leftJoin(qApplyAmountRowDO).on(qApplyAmountRowDO.apply.id.eq(qApplyAmountDO.id))
                    .where( qApplyAmountDO.applyType.ne(ApplyAmountTypeEnum.MATERIAL.getCode()),
                            qApplyAmountDO.status.eq(ApplyAmountStatusEnum.PAID.getCode()),
                            qApplyAmountRowDO.applyPayment.gt(qApplyAmountRowDO.forWriteOffAmount),
                            ExpressionUtils.anyOf(predicates))
                    .fetch();
            // 查询合同的
            canWriteOffDetailEntities.addAll(list);
        }

        // step5: 组装数据
        List<ApplyAmountRowWriteOffRecordSaveVO> finalWriteOffRowRecordEntities = writeOffRowRecordEntities;
        List<ApplyAmountRowDetailVO> rowVO = detailEntities.stream().map(rowDO -> {
            ApplyAmountRowDetailVO row = new ApplyAmountRowDetailVO();
            row.setId(rowDO.getId());
            row.setSourceContractId(Optional.ofNullable(rowDO.getSourceContractId()).orElse(0L));
            row.setApplyNo(rowDO.getApplyNo());
            row.setBillId(rowDO.getBillId());
            row.setBillNo(rowDO.getBillNo());
            row.setBillAbstract(rowDO.getBillAbstract());
            row.setBillType(rowDO.getBillType());
            row.setBillTypeName(ApplyAmountRowBillTypeEnum.getNameByCode(rowDO.getBillType()));
            row.setBillTime(rowDO.getBillTime().format(SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER));
            row.setBillStatus(rowDO.getBillStatus());
            row.setStatus(ApplyAmountStatusEnum.getNameByCode(rowDO.getStatus()));
            // 退款申请单的，金额是对账金额
            row.setBillAmount(rowDO.getBillAmount());
            row.setBillRowAmount(rowDO.getBillRowAmount());
            row.setHasTax(rowDO.getHasTax());
            row.setHasTaxName(CommonBooleanEnum.getMessage(rowDO.getHasTax()));
            row.setTaxRate(rowDO.getTaxRate());
            row.setPaid(rowDO.getPaid());
            row.setAppliedUnpaid(rowDO.getAppliedUnpaid());
            row.setReconciliationAmount(rowDO.getReconciliationAmount());
            row.setApplyPayment(rowDO.getApplyPayment());
            row.setWriteOffAmount(rowDO.getWriteOffAmount());

            // 设置核销记录数据
            List<ApplyAmountRowWriteOffRecordSaveVO> writeOffRecords = Optional.ofNullable(recordMap.get(rowDO.getId())).map(map -> map.stream().map(map1 -> {
                ApplyAmountRowWriteOffRecordSaveVO item = finalWriteOffRowRecordEntities.stream().filter(f -> f.getApplyRowId().equals(map1.getWriteOffRowRecordId())).findFirst().orElse(new ApplyAmountRowWriteOffRecordSaveVO());
                Long id = Optional.ofNullable(item.getId()).orElse(-1L);
                item.setId(id);
                item.setWriteOffAmount(map1.getWriteOffAmount());
                BigDecimal canWriteAmount = Optional.ofNullable(item.getApplyRowPayment()).orElse(BigDecimal.ZERO).subtract(item.getWriteOffAmount());
                item.setCanWriteAmount(canWriteAmount);
                return item;
            }).filter(k  -> k.getId().compareTo(0L) > 0).collect(Collectors.toList())).orElse(new ArrayList<>());
            row.setWriteOffRecords(writeOffRecords);
            // 获取可核销金额
            BigDecimal canWriteOffAmount = canWriteOffDetailEntities.stream()
                    .filter(f -> {
                            boolean res1 = f.getBillId().equals(rowDO.getBillId()) || rowDO.getSourceContractId().equals(f.getBillId());
                            boolean res2 = f.getTaxRate().setScale(2, RoundingMode.HALF_UP).compareTo(rowDO.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0;
                            return res1 && res2;
                    }).map(map -> map.getApplyPayment().subtract(map.getForWriteOffAmount()))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // 补充自己
//            canWriteOffAmount.add(rowDO.getWriteOffAmount());
            row.setCanWriteOffAmount(canWriteOffAmount);
            boolean canWriteOffFlag = canWriteOffAmount.setScale(2, RoundingMode.HALF_UP).compareTo(new BigDecimal("0.00")) >0;
            row.setCanWriteOffFlag(canWriteOffFlag);
            return row;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(detailEntitiesPage.getTotalElements(),rowVO));

    }

    @Override
    public Wrapper<PageData<ApplyAmountOrderVO>> findApplyAmountRowSourceOrders(UserLoginCacheDTO sysUser, OrderBuyerSettlePageVO queryParameter) {
        // step1: 订单服务查询
        Wrapper<PageData<OrderBuyerSettleQueryVO>> dataWrapper = feignService.findApplyAmountRowSourceOrders(queryParameter);
        if (dataWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            Wrapper.fail(ResponseCode.SERVICE_ORDER_ERROR);
        }
        PageData<OrderBuyerSettleQueryVO> dataPage = dataWrapper.getData();
        if (dataPage.getTotalCount().compareTo(0L) == 0) {
            return Wrapper.success(new PageData<>( dataPage.getTotalCount(), new ArrayList<>()));
        }
        // step2: 查询所有单据类型订单、单据id得所有请款明细记录
        List<OrderBuyerSettleQueryVO> dataList = dataPage.getData();
        List<Long> billIds = dataList.stream().map(OrderBuyerSettleQueryVO::getOrderId).collect(Collectors.toList());
        QApplyAmountDO qApplyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO qApplyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        List<ApplyAmountDetailBO> helpDateList = jpaQueryFactory.select(Projections.constructor(ApplyAmountDetailBO.class,
                        qApplyAmountRowDO.billId,
                        qApplyAmountRowDO.billType,
                        qApplyAmountRowDO.taxRate,
                        qApplyAmountDO.status,
                        qApplyAmountRowDO.applyPayment.sum()))
                .from(qApplyAmountDO)
                .leftJoin(qApplyAmountRowDO).on(qApplyAmountRowDO.apply.id.eq(qApplyAmountDO.id))
                .where(qApplyAmountRowDO.billType.eq(ApplyAmountRowBillTypeEnum.ORDER.getCode()),
                        qApplyAmountRowDO.billId.in(billIds))
                .groupBy(qApplyAmountRowDO.billId,
                        qApplyAmountRowDO.billType,
                        qApplyAmountRowDO.taxRate,
                        qApplyAmountDO.status).fetch();
        // step3: 组装数据
        List<ApplyAmountOrderVO> targetList = dataList.stream().map(map -> {
            ApplyAmountOrderVO item = new ApplyAmountOrderVO();
            item.setSourceContractId(map.getContractId());
            item.setBillId(map.getOrderId());
            item.setBillNo(map.getOrderNo());
            item.setBillAbstract(map.getDigest());
            item.setBillType(ApplyAmountRowBillTypeEnum.ORDER.getCode());
            item.setBillTypeName(ApplyAmountRowBillTypeEnum.ORDER.getName());
            item.setBillTime(map.getCreateTime());
            item.setBillStatus(map.getStatusName());
//            item.setBillAmount(map.getAmount());
            item.setBillAmount(map.getProductAmount());
            item.setBillRowAmount(map.getProductAmount());
            Integer hasTax =  map.getTax() ? CommonBooleanEnum.YES.getCode() : CommonBooleanEnum.NO.getCode();
            item.setHasTax(hasTax);
            item.setHasTaxName(CommonBooleanEnum.getMessage(hasTax));
            item.setTaxRate(map.getTaxRate().multiply(BigDecimal.valueOf(100)));
            BigDecimal paid = helpDateList.stream()
                    .filter(f ->f.getStatus().equals(ApplyAmountStatusEnum.PAID.getCode())
                            && f.getBillId().equals(map.getOrderId())
                            && f.getBillType().equals(ApplyAmountRowBillTypeEnum.ORDER.getCode())
                            && f.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(item.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0)
                    .map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal::add)
                    .orElse(new BigDecimal("0.00"));
            BigDecimal appliedUnpaid = helpDateList.stream()
                    .filter(f ->
                            !f.getStatus().equals(ApplyAmountStatusEnum.PAID.getCode())
                            && f.getBillId().equals(map.getOrderId())
                            && f.getBillType().equals(ApplyAmountRowBillTypeEnum.ORDER.getCode())
                            && f.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(item.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0)
                    .map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal::add)
                    .orElse(new BigDecimal("0.00"));
            item.setPaid(paid);
            item.setAppliedUnpaid(appliedUnpaid);
            item.setApplyPayment(item.getBillRowAmount().subtract(paid).subtract(appliedUnpaid));
            return item;
        }).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(dataPage.getTotalCount(), targetList));
    }

    @Override
    public Wrapper<PageData<ApplyAmountContractVO>> findApplyAmountRowSourceContracts(UserLoginCacheDTO sysUser, ContractPageRequest queryParameter) {
        // step1: 合同查询
        queryParameter.setSourceType(ApplyAmountTypeEnum.getContractCodeByCode(queryParameter.getSourceType()));
        Wrapper<PageData<ContractPageResponse>> dataWrapper = feignService.findApplyAmountRowSourceContracts(queryParameter);
        if (dataWrapper.getCode() != ResponseCode.SUCCESS.getCode()) {
            return Wrapper.fail(ResponseCode.CONTRACT_SERVICE_ERROR);
        }
        PageData<ContractPageResponse> dataPage = dataWrapper.getData();
        if (dataPage.getTotalCount().compareTo(0L) == 0) {
            return Wrapper.success(new PageData<>(dataPage.getTotalCount(), new ArrayList<>()));
        }
        // step2: 查询所有单据类型合同、单据id得所有请款明细记录
        List<ContractPageResponse> dataList = dataPage.getData();
        List<Long> billIds = dataList.stream().map(ContractPageResponse::getContractId).distinct().collect(Collectors.toList());
        QApplyAmountDO qApplyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO qApplyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        List<ApplyAmountDetailBO> helpDateList = jpaQueryFactory.select(Projections.constructor(ApplyAmountDetailBO.class,
                        qApplyAmountRowDO.billId,
                        qApplyAmountRowDO.billType,
                        qApplyAmountRowDO.taxRate,
                        qApplyAmountDO.status,
                        qApplyAmountRowDO.applyPayment.sum()))
                .from(qApplyAmountDO)
                .leftJoin(qApplyAmountRowDO).on(qApplyAmountRowDO.apply.id.eq(qApplyAmountDO.id))
                .where(qApplyAmountRowDO.billType.eq(ApplyAmountRowBillTypeEnum.CONTRACT.getCode()),
                        qApplyAmountRowDO.billId.in(billIds))
                .groupBy(qApplyAmountRowDO.billId,
                        qApplyAmountRowDO.billType,
                        qApplyAmountRowDO.taxRate,
                        qApplyAmountDO.status).fetch();
        // 查询所有单据是订单，来源合同的请款明细记录
        List<ApplyAmountDetailBO> helpOrderDateList = jpaQueryFactory.select(Projections.constructor(ApplyAmountDetailBO.class,
                        qApplyAmountRowDO.sourceContractId,
                        qApplyAmountRowDO.billType,
                        qApplyAmountRowDO.taxRate,
                        qApplyAmountDO.status,
                        qApplyAmountRowDO.applyPayment.sum()))
                .from(qApplyAmountDO)
                .leftJoin(qApplyAmountRowDO).on(qApplyAmountRowDO.apply.id.eq(qApplyAmountDO.id))
                .where(qApplyAmountRowDO.billType.eq(ApplyAmountRowBillTypeEnum.ORDER.getCode()),
                        qApplyAmountRowDO.sourceContractId.in(billIds))
                .groupBy(qApplyAmountRowDO.sourceContractId,
                        qApplyAmountRowDO.billType,
                        qApplyAmountRowDO.taxRate,
                        qApplyAmountDO.status).fetch()
                .stream()
                .peek(peek -> peek.setBillType(ApplyAmountRowBillTypeEnum.CONTRACT.getCode()))
                .collect(Collectors.toList());
        helpDateList.addAll(helpOrderDateList);
        // step3: 组装数据
        List<ApplyAmountContractVO> targetList = dataList.stream().map(map -> {
            ApplyAmountContractVO item = new ApplyAmountContractVO();
            item.setBillId(map.getContractId());
            item.setBillNo(map.getContractNo());
            item.setBillAbstract(map.getContractAbstract());
            item.setBillType(ApplyAmountRowBillTypeEnum.CONTRACT.getCode());
            item.setBillTypeName(ApplyAmountRowBillTypeEnum.CONTRACT.getName());
            String BillTime = OrderDateTimeUtil.timestampToLocalDateTime(map.getStartTime()).format(SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER);
            item.setBillTime(BillTime);
            String billEndTime = OrderDateTimeUtil.timestampToLocalDateTime(map.getEndTime()).format(SettleAccountsServiceContants.DEFAULT_DATE_FORMATTER);
            item.setBillEndTime(billEndTime);
            item.setBillStatus(ApplyAmountRowStatusEnum.COMPLETE.getMessage());
            item.setHasTax(map.getIsHasTax());
            item.setHasTaxName(CommonBooleanEnum.getMessage(map.getIsHasTax()));
            item.setTaxRate(BigDecimal.valueOf(map.getTaxRate()));
//            item.setBillAmount(BigDecimal.valueOf(map.getTotalAmount()));
            item.setBillAmount(BigDecimal.valueOf(map.getTaxAmount()));
            item.setBillRowAmount(BigDecimal.valueOf(map.getTaxAmount()));
            BigDecimal paid = helpDateList.stream()
                    .filter(f -> f.getStatus().equals(ApplyAmountStatusEnum.PAID.getCode())
                            && f.getBillId().equals(map.getContractId())
                            && f.getBillType().equals(ApplyAmountRowBillTypeEnum.CONTRACT.getCode())
                            && f.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(item.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0)
                    .map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal::add)
                    .orElse(new BigDecimal("0.00"));
            BigDecimal appliedUnpaid = helpDateList.stream()
                    .filter(f -> !f.getStatus().equals(ApplyAmountStatusEnum.PAID.getCode())
                            && f.getBillId().equals(map.getContractId())
                            && f.getBillType().equals(ApplyAmountRowBillTypeEnum.CONTRACT.getCode())
                            && f.getTaxRate().setScale(2, RoundingMode.HALF_UP)
                            .compareTo(item.getTaxRate().setScale(2, RoundingMode.HALF_UP)) == 0)
                    .map(ApplyAmountDetailBO::getApplyPayment)
                    .reduce(BigDecimal::add)
                    .orElse(new BigDecimal("0.00"));
            BigDecimal remainAmount = item.getBillRowAmount().subtract(paid).subtract(appliedUnpaid);
            item.setRemainAmount(remainAmount);
            item.setPaid(paid);
            item.setAppliedUnpaid(appliedUnpaid);
            return item;
        }).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(dataPage.getTotalCount(), targetList));
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Wrapper<Void> pay(Long applyAmountId) {
        // step1: 查询请款单
        ApplyAmountDO entity = repository.findById(applyAmountId).orElse(null);
        if(entity == null) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_APPLY_AMOUNT_UPDATE_NO_PERMISSION);
        }
        // step2: 修改请款单状态
        entity.setStatus(ApplyAmountStatusEnum.PAID.getCode());
        entity.setPayTime(LocalDateTime.now());
        repository.saveAndFlush(entity);
        // step3: 由对账单推送更新订单收发货批次的结算标志为
        if (entity.getApplyType().equals(ApplyAmountTypeEnum.MATERIAL.getCode())) {
            reconciliationService.pay(applyAmountId, entity.getReconciliationId());
        }
        return Wrapper.success();
    }

    @Override
    public Wrapper<Void> addInvoiceRecord(Long applyAmountId, Long invoiceId, Long proofId, List<InvoiceProveBO> proveList) {
        // step1: 查询请款单
        ApplyAmountDO entity = repository.findById(applyAmountId).orElse(null);
        if (entity == null) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        if (!ApplyAmountTypeEnum.MATERIAL.getCode().equals(entity.getApplyType())) {
            return Wrapper.fail(ResponseCode.SA_MEMBER_BUSINESS_RECONCILIATION_NOT_EXIST);
        }
        return reconciliationService.addInvoiceRecord(entity.getReconciliationId(), invoiceId, proofId, proveList);
    }

    private List<ApplyAmountRowWriteOffRecordDO> getWriteOffRecordDOS(List<ApplyAmountRowDO> detailEntities) {
        Specification<ApplyAmountRowWriteOffRecordDO> recordSpecification = (root, query, cb) -> {
            CriteriaBuilder.In<ApplyAmountRowDO> in = cb.in(root.get("applyRow").as(ApplyAmountRowDO.class));
            detailEntities.forEach(in::value);
            return in;
        };
        return detailRecordRepository.findAll(recordSpecification);
    }

    @Override
    public Wrapper<PageData<ApplyAmountDetailSunVO>> pageContractExecuteDetail(ApplyAmountDetailSunQueryVO queryVO) {
        // step 1:根据查询条件过滤要查询的订单
        Specification<ApplyAmountRowDO> spec = (root, query, builder) -> {
            List<Predicate> queryList = new ArrayList<>();
            Predicate sourceContractId = builder.equal(root.get("sourceContractId").as(Long.class), queryVO.getContractId());
            queryList.add(sourceContractId);
            if (StringUtils.hasText(queryVO.getOrderNo())) {
                Predicate billNo = builder.like(root.get("billNo").as(String.class), queryVO.getOrderNo());
                queryList.add(billNo);
            }
            if (StringUtils.hasText(queryVO.getOrderAbstract())) {
                Predicate billAbstract = builder.like(root.get("billAbstract").as(String.class), queryVO.getOrderAbstract());
                queryList.add(billAbstract);
            }
            if (StringUtils.hasText(queryVO.getStartTime())) {
                Predicate billTime = builder.greaterThanOrEqualTo(root.get("billTime").as(LocalDateTime.class), LocalDateTime.parse(queryVO.getStartTime(), SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
                queryList.add(billTime);
            }
            if (StringUtils.hasText(queryVO.getEndTime())) {
                Predicate billTime = builder.lessThanOrEqualTo(root.get("billTime").as(LocalDateTime.class), LocalDateTime.parse(queryVO.getEndTime(), SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
                queryList.add(billTime);
            }
            return query.where(queryList.toArray(new Predicate[0])).getRestriction();
        };
        List<ApplyAmountRowDO> details = detailRepository.findAll(spec);
        if (CollectionUtils.isEmpty(details)) {
            Wrapper.success(new PageData<>(0L, new ArrayList<>()));
        }
        List<Long> billIds = details.stream().map(ApplyAmountRowDO::getBillId).distinct().collect(Collectors.toList());
        // step 2:查询统计付款的请款单、已请款为付款的数据分页数据
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        NumberExpression<BigDecimal> unPayApplyAmount = Expressions.cases().when(applyAmountDO.status.eq(ApplyAmountStatusEnum.PAID.getCode())).then(applyAmountRowDO.reconciliationAmount).otherwise(BigDecimal.ZERO);
        NumberExpression<BigDecimal> unApplyAmount = Expressions.cases().when(applyAmountDO.status.ne(ApplyAmountStatusEnum.PAID.getCode())).then(applyAmountRowDO.reconciliationAmount).otherwise(BigDecimal.ZERO);
        JPAQuery<ApplyAmountDetailSunVO> jpaQuery = jpaQueryFactory.select(Projections.constructor(ApplyAmountDetailSunVO.class, applyAmountRowDO.billId,  applyAmountRowDO.billRowAmount, applyAmountRowDO.taxRate, unPayApplyAmount.sum(), unApplyAmount.sum()))
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                .where(applyAmountRowDO.billId.in(billIds), applyAmountRowDO.sourceContractId.eq(queryVO.getContractId()));
//        if (!queryVO.getPartyAFlag()) {
//            jpaQuery.where(applyAmountDO.status.eq(ApplyAmountStatusEnum.PAID.getCode()));
//        }
        jpaQuery.groupBy(applyAmountRowDO.billId, applyAmountRowDO.billRowAmount, applyAmountRowDO.taxRate);
        // step 3:查询统计数
        long size = jpaQuery.fetch().size();
        List<ApplyAmountDetailSunVO> list = jpaQuery.limit(queryVO.getPageSize()).offset(queryVO.getCurrentOffset()).fetch();
        // step 4:组装数据
        list.forEach(item -> {
            Optional<ApplyAmountRowDO> first = details.stream().filter(f -> f.getBillId().equals(item.getOrderId())).findFirst();
            item.setOrderNO(first.map(ApplyAmountRowDO::getBillNo).orElse(""));
            item.setOrderAbstract(first.map(ApplyAmountRowDO::getBillAbstract).orElse(""));
            item.setOrderType(first.map(ApplyAmountRowDO::getBillType).orElse(-1));
            item.setOrderTypeName(ApplyAmountRowBillTypeEnum.getNameByCode(item.getOrderType()));
            item.setOrderStatusName(first.map(ApplyAmountRowDO::getBillStatus).orElse(""));
            item.setOrderTime(first.map(ApplyAmountRowDO::getBillTime).map(OrderDateTimeUtil::localDateTimeToTimestamp).orElse(0L));
            item.setHasTax(first.map(ApplyAmountRowDO::getHasTax).orElse(0));
        });
        return Wrapper.success(new PageData<>(size, list));
    }

    @Override
    public Wrapper<List<ApplyAmountDetailTotalSunVO>> pageContractExecuteDetailSum(ApplyAmountDetailTotalSunQueryVO queryVO) {
        // step1: 组装查询条件
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        NumberExpression<BigDecimal> unPayApplyAmount = Expressions.cases().when(applyAmountDO.status.eq(ApplyAmountStatusEnum.PAID.getCode())).then(applyAmountRowDO.reconciliationAmount).otherwise(BigDecimal.ZERO);
        NumberExpression<BigDecimal> unApplyAmount = Expressions.cases().when(applyAmountDO.status.ne(ApplyAmountStatusEnum.PAID.getCode())).then(applyAmountRowDO.reconciliationAmount).otherwise(BigDecimal.ZERO);
        List<ApplyAmountDetailTotalSunVO> list = jpaQueryFactory.select(Projections.constructor(ApplyAmountDetailTotalSunVO.class, applyAmountRowDO.sourceContractId, unPayApplyAmount.sum(), unApplyAmount.sum()))
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                .where(applyAmountRowDO.sourceContractId.in(queryVO.getContractIds()))
                .groupBy(applyAmountRowDO.sourceContractId)
                .fetch();
        return Wrapper.success(list);
    }


    @Override
    public Wrapper<PageData<ApplyAmountSunVO>> pageListForSummaryByParty(ApplyAmountSunQueryVO queryVO) {
        // step1: 构建查询条件
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        JPAQuery<ApplyAmountDO> jpaQuery = jpaQueryFactory.select(applyAmountDO).distinct()
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                .where(applyAmountRowDO.sourceContractId.eq(queryVO.getContractId()));


        // step 2:查询统计数
        long total = jpaQuery.fetchCount();
        // sept 3: 查询数据
        List<ApplyAmountDO> list = jpaQuery.limit(queryVO.getPageSize()).offset(queryVO.getCurrentOffset()).fetch();
        // step3: 组装数据
        List<ApplyAmountSunVO> resultList = list.stream().map(map -> {
            ApplyAmountSunVO item = new ApplyAmountSunVO();
            item.setId(map.getId());
            item.setApplyNo(map.getApplyNo());
            item.setApplyAbstract(map.getApplyAbstract());
            item.setStatus(map.getStatus());
            item.setStatusName(ApplyAmountStatusEnum.getNameByCode(map.getStatus()));
            item.setApplyTime(map.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_MINUTE_TIME_FORMATTER));
            item.setApplyAmount(map.getApplyAmount().doubleValue());
            item.setPayTime(Optional.ofNullable(map.getPayTime()).map(help -> help.format(SettleAccountsServiceContants.DEFAULT_MINUTE_TIME_FORMATTER)).orElse(""));
            item.setPayAmount(map.getApplyAmount().doubleValue());
            item.setPayWayName(PurchaseContractPayTypeEnum.getNameByCode(map.getPayWay()));
            return item;
        }).collect(Collectors.toList());
        return Wrapper.success(new PageData<>(total, resultList));
    }

    @Override
    public Wrapper<List<ApplyAmountSunVO>> listForPaySummaryByParty(ApplyAmountSunQueryVO queryVO) {
        // step1: 构建查询条件
        QApplyAmountDO applyAmountDO = QApplyAmountDO.applyAmountDO;
        QApplyAmountRowDO applyAmountRowDO = QApplyAmountRowDO.applyAmountRowDO;
        JPAQuery<ApplyAmountDO> jpaQuery = jpaQueryFactory.select(applyAmountDO).distinct()
                .from(applyAmountDO)
                .leftJoin(applyAmountRowDO).on(applyAmountRowDO.apply.id.eq(applyAmountDO.id))
                .where(applyAmountRowDO.sourceContractId.eq(queryVO.getContractId()));
        if (Optional.ofNullable(queryVO.getPartyAFlag()).map(map -> !map).orElse(true)) {
            jpaQuery.where(applyAmountDO.status.eq(ApplyAmountStatusEnum.PAID.getCode()));
        }
        List<ApplyAmountDO> list = jpaQuery.fetch();
        List<ApplyAmountSunVO> resultList = list.stream().map(map -> {
            ApplyAmountSunVO item = new ApplyAmountSunVO();
            item.setId(map.getId());
            item.setApplyNo(map.getApplyNo());
            item.setApplyAbstract(map.getApplyAbstract());
            item.setStatus(map.getStatus());
            item.setStatusName(ApplyAmountStatusEnum.getNameByCode(map.getStatus()));
            item.setApplyTime(map.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_MINUTE_TIME_FORMATTER));
            item.setApplyAmount(map.getApplyAmount().doubleValue());
            item.setPayTime(Optional.ofNullable(map.getPayTime()).map(help -> help.format(SettleAccountsServiceContants.DEFAULT_MINUTE_TIME_FORMATTER)).orElse(""));
            item.setPayAmount(map.getApplyAmount().doubleValue());
            item.setPayWayName(PurchaseContractPayTypeEnum.getNameByCode(map.getPayWay()));
            return item;
        }).collect(Collectors.toList());
        return  Wrapper.success(resultList);
    }

    /**
     * 请款单分页列表数据组装
     * @param result      分页请款单实体
     */
    private Wrapper<PageData<ApplyAmountPageVO>> assembleData(Page<ApplyAmountDO> result){

        List<ApplyAmountPageVO> resultList = result.getContent().stream().map(r ->{
            ApplyAmountPageVO vo = new ApplyAmountPageVO();
            vo.setId(r.getId());
            vo.setApplyNo(r.getApplyNo());
            vo.setApplyAbstract(r.getApplyAbstract());
            vo.setApplyType(r.getApplyType());
            vo.setApplyTypeName(ApplyAmountTypeEnum.getNameByCode(r.getApplyType()));
            vo.setPayer(r.getPayer());
            vo.setPayee(r.getPayee());
            vo.setApplyAmount(r.getApplyAmount());
            vo.setExpectPayTime(r.getExpectPayTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setCreateTime(r.getCreateTime().format(SettleAccountsServiceContants.DEFAULT_TIME_FORMATTER));
            vo.setStatus(r.getStatus());
            vo.setStatusName(ApplyAmountStatusEnum.getNameByCode(r.getStatus()));
            boolean hasSubmit = !r.getApplyType().equals(ApplyAmountTypeEnum.MATERIAL.getCode()) && r.getStatus().equals(ApplyAmountStatusEnum.TO_SUBMIT.getCode());
            vo.setHasSubmit(hasSubmit);
            return vo;
        }).collect(Collectors.toList());

        return Wrapper.success(new PageData<>(result.getTotalElements(),resultList));
    }



}
